/* hlite.c, generic syntax highlighting, Ait Emacs, Kevin Bloom, BSD 3-Clause, 2023-2025 */

#include "header.h"
#include "util.h"

int state = ID_DEFAULT;
int next_state = ID_DEFAULT;
int skip_count = 0;
int exclude_state = ID_DEFAULT;
int exclude_count = 0;

char_t get_at(buffer_t *bp, point_t pt)
{
  return (*ptr(bp, pt));
}

void set_parse_state(buffer_t *bp, point_t pt, window_t *wp, int loop)
{
  register point_t po;

  state = ID_DEFAULT;
  next_state = ID_DEFAULT;
  skip_count = 0;

  if(bp->b_mode != NULL && loop) {
    for (po =0; po < pt; po++)
        parse_text(bp, po, TRUE);
    wp->w_hilite = state;
  }
}

void write_parse_state(window_t *wp)
{
  state = wp->w_hilite;
  next_state = wp->w_hilite;
  skip_count = 0;
}

/* we don't bother running the syntax logic on space chars
   therefore we must manually decrease the skip_count if we are
   in a syntax highlight that includes space chars.
*/
void dec_skip()
{
  skip_count--;
}

/* quick set to TRUE causes the syntax highlighting for keywords to
   be disabled. The purpose is so that when we have to determine the
   state at b_page we can do it quicker and reduce potential lag.
*/
int parse_text(buffer_t *bp, point_t pt, int quick)
{
//  if(bp->b_mode == NULL)
//    return state;

  if (skip_count-- > 0) {
    if(exclude_count != 0)
      exclude_count--;
    if(exclude_state != ID_DEFAULT &&
       exclude_count == 0) {
       state = exclude_state;
       exclude_state = ID_DEFAULT;
    }
    return state;
  }

  char_t c_now = get_at(bp, pt);
  char_t c_prev = get_at(bp, pt-1);
  char_t next = c_now;
  int valid = TRUE, k = 0;
  state = next_state;

  if (state == ID_DEFAULT &&
      bp->b_mode != NULL &&
      bp->b_mode->mlc != NULL) {
    next = c_now;
    for(int i = 0; bp->b_mode->mlc[i] != '\0'; i++) {
      next = get_at(bp, pt + i);
      if(next != bp->b_mode->mlc[i]) {
        valid = FALSE;
        break;
      }
    }
    if(valid) {
      skip_count = 1;
      return (next_state = state = ID_BLOCK_COMMENT);
    }
    valid = TRUE;
  }

  if (state == ID_BLOCK_COMMENT &&
      bp->b_mode != NULL &&
      bp->b_mode->emlc != NULL) {
    next = c_now;
    for(int i = 0; bp->b_mode->emlc[i] != '\0'; i++) {
      next = get_at(bp, pt + i);
      if(next != bp->b_mode->emlc[i]) {
        valid = FALSE;
        break;
      }
    }
    if(valid) {
      skip_count = strlen(bp->b_mode->emlc) - 1;
      next_state = ID_DEFAULT;
      return ID_BLOCK_COMMENT;
    }
    valid = TRUE;
  }

  if (state == ID_DEFAULT &&
      bp->b_mode != NULL &&
      bp->b_mode->slc != NULL &&
      bp->b_mode->slc[0] != '\0') {
    next = c_now;
    for(int i = 0; bp->b_mode->slc[i] != '\0'; i++) {
      next = get_at(bp, pt + i);
      if(next != bp->b_mode->slc[i]) {
        valid = FALSE;
        break;
      }
    }
    if(valid) {
      return (next_state = state = ID_LINE_COMMENT);
    }
    valid = TRUE;
  }

  if (state == ID_LINE_COMMENT && c_now == '\n')
    return (next_state = ID_DEFAULT);

  if (state == ID_DEFAULT && c_now == '"') {
    int enable = FALSE;
    char_t z = get_at(bp, pt+1);
    point_t end = pos(bp, bp->b_ebuf);
    for(point_t i = pt+1; z != '\n' && i <= end; i++, z = get_at(bp, i)) {
      if(z == '"') {
        enable = TRUE;
        break;
      }
      if((bp->b_mode != NULL && !bp->b_mode->bmls) || (z == '\\' && get_at(bp, i+1) == '\n')) {
        enable = TRUE;
        break;
      }
    }
    if(enable)
      return (next_state = ID_DOUBLE_STRING);
  }

  if (state == ID_DEFAULT &&
      bp->b_mode != NULL &&
      bp->b_mode->bqas &&
      c_now == '`')
    return (next_state = ID_BACK_STRING);

  if (state == ID_DEFAULT &&
      bp->b_mode != NULL &&
      bp->b_mode->sqas &&
      c_now == '\'') {
      int enable = FALSE;
    char_t z = get_at(bp, pt+1);
    point_t end = pos(bp, bp->b_ebuf);
    for(point_t i = pt+1; z != '\n' && i <= end; i++, z = get_at(bp, i)) {
      if(z == '\'') {
        enable = TRUE;
        break;
      }
    }
    if(enable)
      return (next_state = ID_SINGLE_STRING);
  }

  if (state == ID_DOUBLE_STRING && c_now == '\\') {
    skip_count = 1;
    return (next_state = ID_DOUBLE_STRING);
  }

  if (state == ID_DOUBLE_STRING && c_now == '"') {
    next_state = ID_DEFAULT;
    return ID_DOUBLE_STRING;
  }


  if (state == ID_SINGLE_STRING && c_now == '\\') {
    skip_count = 1;
    return (next_state = ID_SINGLE_STRING);
  }

  if (state == ID_DEFAULT &&
      bp->b_mode != NULL &&
      bp->b_mode->bqas &&
      c_now == '`')
    return (next_state = ID_BACK_STRING);

  if (state == ID_BACK_STRING && c_now == '\\') {
    skip_count = 1;
    return (next_state = ID_BACK_STRING);
  }


  if (state == ID_SINGLE_STRING && c_now == '\'') {
    next_state = ID_DEFAULT;
    return ID_SINGLE_STRING;
  }

  if (state == ID_BACK_STRING && c_now == '`') {
    next_state = ID_DEFAULT;
    return ID_BACK_STRING;
  }

  point_t ep = pos(bp, bp->b_ebuf);
  int sub = 1;

  if(bp->b_mode != NULL && !quick &&
     bp->b_mode->keywords != NULL &&
     state == ID_DEFAULT) {
     for(int i = 0; bp->b_mode->keywords[i].word != NULL; i++) {
       int l = 0, t = 0;
       k = 0;
       sub = 1;
       exclude_count = 0;
       exclude_state = ID_DEFAULT;
       if(bp->b_mode->keywords[i].word[l] != '' && (pt == 0 ||
       (is_symbol(c_prev) &&
         (c_prev != '-' && c_prev != '_'))
       || isspace(c_prev))) {
         // do nothing
       } else if(bp->b_mode->keywords[i].word[l] == '') {
         l++;
       } else {
         return (state = ID_DEFAULT);
       }
       if(bp->b_mode->keywords[i].word[l] == '') {
          if(c_prev != '\n' && pt != 0)
            return (state = ID_DEFAULT);
         l++;
       }
       for(k = 0; bp->b_mode->keywords[i].word[l] != '\0'; k++, l++) {
         c_now = get_at(bp, pt+k);
         /* at the end */
         if(bp->b_mode->keywords[i].word[l] == '') {
            l++;
            if(bp->b_mode->keywords[i].word[l] == '\0') {
              for(; c_now != '\n' && pt+k != ep; k++) {
                c_now = get_at(bp, pt+k);
              }
              k--;
              break;
            } else if(bp->b_mode->keywords[i].word[l] > 32) {
              for(; c_now != bp->b_mode->keywords[i].word[l] && pt+k != ep; k++) {
                c_now = get_at(bp, pt+k);
              }
              k--;
            } else if(bp->b_mode->keywords[i].word[l] == '') {
              for(; c_now != '\n' && pt+k != ep ; k++) {
                c_now = get_at(bp, pt+k);
                if(bp->b_mode->keywords[i].word[l] == '' &&
                   bp->b_mode->keywords[i].word[l+1] == c_now) {
                  t = 2;
                  break;
                }
              }
              if(t == 0) {
                k = 0;
                break;
              }
              if(t == 2) {
                l++;
                k--;
                sub++;
                continue;
              }
            }
         }
         if(bp->b_mode->keywords[i].word[l] == '' ||
            bp->b_mode->keywords[i].word[l] == '') {
          int all = bp->b_mode->keywords[i].word[l] == '';
          if(bp->b_mode->keywords[i].word[l+1] == '\0') {
            for(; !isspace(c_now) &&
                  (all ? TRUE : !is_symbolis(
                    c_now,
                    bp->b_mode->saiv
                   ));
                   k++) {
              c_now = get_at(bp, pt+k);
            }
            k--;
            break;
          } else {
            l++;
            if(all) {
              for(; !isspace(c_now); k++) {
                if(bp->b_mode->keywords[i].word[l] == c_now) {
                  t = 1;
                  break;
                }
                if(bp->b_mode->keywords[i].word[l] == '' &&
                   bp->b_mode->keywords[i].word[l+1] == c_now) {
                  t = 2;
                  break;
                }
                if(bp->b_mode->keywords[i].word[l] == '' &&
                   isspace(get_at(bp, pt+k))) {
                  t = 3;
                  break;
                }
                if(pt+k == ep)
                  break;
                c_now = get_at(bp, pt+k);
              }
            } else {
              for(; !isspace(c_now) &&
                    (bp->b_mode->keywords[i].word[l] < 32 ||
                     !is_symboli(
                        c_now,
                      bp->b_mode->keywords[i].word[l]
                      ));
                    k++) {
                if(bp->b_mode->keywords[i].word[l] == c_now) {
                  t = 1;
                  break;
                }
                if(bp->b_mode->keywords[i].word[l] == '' &&
                   bp->b_mode->keywords[i].word[l+1] == c_now) {
                  t = 2;
                  break;
                }
                if(bp->b_mode->keywords[i].word[l] == '' &&
                   isspace(get_at(bp, pt+k))) {
                  t = 3;
                  break;
                }
                if(pt+k == ep)
                  break;
                c_now = get_at(bp, pt+k);
              }
            }
            if(t == 0) {
              k = 0;
              break;
            }
            if(t == 1) {
              k--;
              continue;
            }
            if(t == 2) {
              l++;
              k--;
              sub++;
              continue;
            }
            if(t == 3) {
              // do nothing
            }
          }
         }
         if(bp->b_mode->keywords[i].word[l] == '') {
           l++;
           c_now = get_at(bp, pt+k);
           for(; isspace(c_now) && pt+k != ep; k++) {
             c_now = get_at(bp, pt+k);
           }
           k--;
           c_now = get_at(bp, pt+k);
         }
         if(bp->b_mode->keywords[i].word[l] == '') {
           if(bp->b_mode->keywords[i].word[l+1] == c_now) {
             sub++;
             l++;
             continue;
           } else {
             k = 0;
             exclude_count = 0;
             break;
           }
         }
         if(bp->b_mode->keywords[i].word[l] == '') {
           if(bp->b_mode->keywords[i].word[l+1] == c_now) {
             exclude_state = bp->b_mode->keywords[i].color;
             exclude_count++;
             k--;
             continue;
           } else {
             k = 0;
             exclude_count = 0;
             break;
           }
         }
         if(bp->b_mode->keywords[i].word[l] != c_now) {
           k = 0;
           break;
         }
       }
      c_now = get_at(bp, pt+k);
      int ignoreAfter = FALSE;
      if(is_symbol(bp->b_mode->keywords[i].word[l])) {
        ignoreAfter = TRUE;
      }
      if(k > 0 && (ignoreAfter || (isspace(c_now) ||
         (is_symbol(c_now) &&
          (c_now != '-' && (
           bp->b_mode->keywords[i].word[l] == '_' || c_now != '_'))))) &&
           (bp->b_mode->keywords[i].word[l] == '\0' ||
            bp->b_mode->keywords[i].word[l+1] == '\0')) {
         skip_count = k-sub;
         next_state = ID_DEFAULT;
         if(exclude_state != ID_DEFAULT) {
           return (state = ID_DEFAULT);
         }
         return (state = bp->b_mode->keywords[i].color);
      }
    }
  }

  if (state != ID_DEFAULT)
    return (next_state = state);

//  if (state == ID_DEFAULT && c_now >= '0' && c_now <= '9') {
//    next_state = ID_DEFAULT;
//    return (state = ID_DIGITS);
//  }

//  if (state == ID_DEFAULT && 1 == is_symbol(c_now)) {
//    next_state = ID_DEFAULT;
//    return (state = ID_SYMBOL);
//  }

  return (next_state = state);
}
